package com.codingchili.logging.controller;

import com.codingchili.logging.Service;
import com.codingchili.logging.configuration.LogContext;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.MessageProducer;
import io.vertx.core.json.JsonObject;

import java.util.Set;

import com.codingchili.core.configuration.CoreStrings;
import com.codingchili.core.listener.CoreHandler;
import com.codingchili.core.listener.Request;
import com.codingchili.core.logging.ConsoleLogger;
import com.codingchili.core.protocol.*;
import com.codingchili.core.security.Token;

import static com.codingchili.common.Strings.*;
import static com.codingchili.core.context.FutureHelper.untyped;
import static com.codingchili.core.protocol.RoleMap.PUBLIC;


/**
 * @author Robin Duda
 * <p>
 * Log handler for log messages incoming from services.
 */
@Roles(PUBLIC)
public class ServiceLogHandler implements CoreHandler {
    private static final Set<String> processable = Service.processableEventNames();
    private final Protocol<Request> protocol = new Protocol<>(this);
    private final ConsoleLogger console;
    private final LogContext context;
    private MessageProducer<JsonObject> statistics;
    private StorageLogger store;

    public ServiceLogHandler(LogContext context) {
        this.context = context;
        this.console = new ConsoleLogger(getClass());
    }

    @Override
    public void start(Promise<Void> start) {
        context.storage(context.service().getServer())
                .onSuccess(db -> this.store = new StorageLogger(context, getClass(), db))
                .onComplete(untyped(start));

        this.statistics = context.bus().publisher(CoreStrings.ID_STATISTICS);
    }

    @Api
    public void ping(Request request) {
        request.accept();
    }

    @Api
    protected void logging(Request request) {
        JsonObject logdata = request.data().getJsonObject(ID_MESSAGE);

        // don't verify tokens for now.
        verifyToken(logdata)
                .onSuccess((v) -> log(logdata))
                .onFailure((e) -> log(logdata));
    }

    private void log(JsonObject logdata) {
        String node = logdata.getString(LOG_NODE);
        if (!NODE_LOGGING.equals(node) && context.consoleEnabled()) {
            console.log(logdata.copy());
        }
        store.log(logdata);
        process(logdata);
    }

    private void process(JsonObject logdata) {
        if (processable.contains(logdata.getString(CoreStrings.LOG_EVENT))) {
            statistics.write(logdata);
        }
    }

    // implement when logger metadata supports complex objects.
    private Future<Void> verifyToken(JsonObject logdata) {
        if (logdata.containsKey(ID_TOKEN)) {
            return context.verifyServerToken(Serializer.unpack(logdata.getJsonObject(ID_TOKEN), Token.class));
        } else {
            return Future.failedFuture("Request is missing token.");
        }
    }

    @Override
    public void handle(Request request) {
        protocol.process(request);
    }

    @Override
    public String address() {
        return NODE_LOGGING;
    }
}
